#include "../Headers/dep_list.h"

/* REFERENCE
struct dep_list {
    symbol dep_name;
    struct dep_list* next;
    struct dep_list_2* caller_index;
};

struct dep_list_2 {
    cluster* caller;
    struct dep_list_2* next;
    struct dep_list_3* time_index;
};

struct dep_list_3 {
    cl_time_t time;
    cluster* name_in_caller;
    struct dep_list_3* next;
};
*/

dep_list*
find_dep_name(dep_list* list, symbol name) {
    while (list != NULL) {
        if (list->dep_name == name) break;
        list = list->next;
    }
    return list;
}

dep_list*
last_dep_name(dep_list* list) {
    if (list == NULL)
        return list;
    while (list->next != NULL)
        list = list->next;
    return list;
}

dep_list*
new_dep_list() {
    dep_list* retval;
    retval = (dep_list*)malloc(sizeof(dep_list));
    retval->dep_name = NULL;
    retval->next = NULL;
    retval->caller_index = NULL;
    return retval;
}

dep_list_2*
find_caller(dep_list_2* list, cluster* caller) {
    while (list != NULL) {
        if (list->caller == caller) break;
        list = list->next;
    }
    return list;
}

dep_list_2*
last_caller(dep_list_2* list) {
    if (list == NULL)
        return list;
    while (list->next != NULL)
        list = list->next;
    return list;
}

dep_list_2*
new_dep_list_2() {
    dep_list_2* retval;
    retval = (dep_list_2*)malloc(sizeof(dep_list_2));
    retval->caller = NULL;
    retval->next = NULL;
    retval->time_index = NULL;
    return retval;
}

dep_list_3*
find_time(dep_list_3* list, cl_time_t time) {
    while (list != NULL) {
        if (list->time == time) break;
        list = list->next;
    }
    return list;
}

dep_list_3*
last_time(dep_list_3* list) {
    if (list == NULL)
        return list;
    while (list->next != NULL)
        list = list->next;
    return list;
}

dep_list_3*
new_dep_list_3() {
    dep_list_3* retval;
    retval = (dep_list_3*)malloc(sizeof(dep_list_3));
    retval->time = TIME_ERROR;
    retval->next = NULL;
    retval->name_in_caller = NULL;
    return retval;
}

/*
 * The equivalent to "callee.dep_list["dep_name"][caller][time] = caller_connect"
 */
void
cl_set_dep(cluster* callee, symbol dep_name, cluster* caller, cl_time_t time, cluster* caller_connect) {
    dep_list* cursor1;
    dep_list_2* cursor2;
    dep_list_3* cursor3;

    cursor1 = find_dep_name(callee->dependencies, dep_name);
    if (!cursor1) {
        cursor1 = last_dep_name(callee->dependencies);
        // The whole list was missing. This should only happen if the dependencies of the container
        // cluster is being created.
        if (!cursor1) {
            assert(strcmp(dep_name, caller_connect->name) == 0);
            callee->dependencies = new_dep_list();
            cursor1 = callee->dependencies;
        } else {  // The dep_name didn't exist.
            cursor1->next = new_dep_list();
            cursor1 = cursor1->next;
        }
        cursor1->dep_name = (char*)malloc((strlen(dep_name) + 1) * sizeof(char));
        strcpy(cursor1->dep_name, dep_name);
    }

    cursor2 = find_caller(cursor1->caller_index, caller);
    if (!cursor2) {
        cursor2 = last_caller(cursor1->caller_index);
        if (!cursor2) {  // The whole list is missing.
            cursor1->caller_index = new_dep_list_2();
            cursor2 = cursor1->caller_index;
        } else {  // The dep_name doesn't exist.
            cursor2->next = new_dep_list_2();
            cursor2 = cursor2->next;
        }
        cursor2->caller = caller;
    }

    cursor3 = find_time(cursor2->time_index, time);
    if (!cursor3) {
        cursor3 = last_time(cursor2->time_index);
        if (!cursor3) {
            cursor2->time_index = new_dep_list_3();
            cursor3 = cursor2->time_index;
        } else {
            cursor3->next = new_dep_list_3();
            cursor3 = cursor3->next;
        }
        cursor3->time = time;
    }

    cursor3->name_in_caller = caller_connect;
}

cluster*
cl_get_dep(cluster* callee, symbol dep_name, cluster* caller, cl_time_t time) {
    dep_list* cursor1;
    dep_list_2* cursor2;
    dep_list_3* cursor3;

    // Each of these return NULL if NULL is provided, so 
    cursor1 = find_dep_name(callee->dependencies, dep_name);
    cursor2 = find_caller(cursor1->caller_index, caller);
    cursor3 = find_time(cursor2->time_index, time);

    if (cursor3)
        return cursor3->name_in_caller;
    else  // This should add a job to the job_list, block, and then wait for a callback.
        return NULL;
}

void
cl_clear_dep_list(cluster* cl) {
    assert(0);
}

void
cl_clear_dep_list_by_cluster(cluster* callee, cluster* caller) {
    assert(0);
}
